// -*-mode:c++; coding:utf-8-*-

#ifndef _XBASE_PROTOCOL_PACKET_HPP_
#define _XBASE_PROTOCOL_PACKET_HPP_

#include "version.hpp"
#include "error_code.hpp"

#include "protocol_base.hpp"
#include "protocol_struct.hpp"

namespace xbase
{
	namespace protocol
	{
		struct request
		{
			uint32_t m_program_version;

			request()
				: m_program_version(version) {}

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return in.load(&m_program_version);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return out.save(m_program_version);
			}

			std::string to_string() const;
		};
	
		struct response
		{
			uint32_t m_error_code;

			response()
				: m_error_code(error::ok) {}

			error::error_code error_code() const {
				return static_cast<error::error_code>(m_error_code);
			}

			void set_error_code(error::error_code ec) {
				m_error_code = static_cast<uint32_t>(ec);
			}

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return in.load(&m_error_code);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return out.save(m_error_code);
			}

			std::string to_string() const;
		};

		struct root_meta_request
			: public request
		{
			static const type_code type = ROOT_META_REQ;
		};

		struct root_meta_response
			: public response
		{
			static const type_code type = ROOT_META_RESP;

			tablet_info m_tablet;
			server_address m_server_address;

			template<typename IArchive>
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_tablet) &&
					in.load(&m_server_address);
			}

			template<typename OArchive>
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_tablet) &&
					out.save(m_server_address);
			}

			std::string to_string() const;
		};

		struct write_request
			: public request
		{
			static const type_code type = WRITE_REQ;
			
			std::string m_tablet_name;
			record_vector m_records;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_records);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_records);
			}

			std::string to_string () const;
		};

		struct write_response
			: public response
		{
			static const type_code type = WRITE_RESP;
			
			std::string m_tablet_name;
			uint32_t m_record_number;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_record_number);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_record_number);
			}

			std::string to_string() const;
		};

		struct write_request_v2
			: public request
		{
			static const type_code type = WRITE_REQ;

			std::string m_tablet_name;
			row_mutation_data_ptr m_mutation_data;
			
			uint64_t m_timestamp;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return false;
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_tablet_name) &&
					m_mutation_data->save(out, m_timestamp);
			}

			std::string to_string() const;
		};

		struct write_request_v2_for_tabletserver
			: public request
		{
			static const type_code type = WRITE_REQ;

			std::string m_tablet_name;
			row_mutation_data_for_tabletserver m_mutation_data;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_mutation_data);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return false;
			}

			std::string to_string() const;
		};

		struct write_response_v2
			: public response
		{
			static const type_code type = WRITE_RESP;
		};

		struct table_info_request
			: public request
		{
			static const type_code type = TABLE_INFO_REQ;
			
			std::string m_table_name;

			table_info_request() {}
			
			explicit
			table_info_request(const std::string &table_name)
				: m_table_name(table_name) {}

			template<typename IArchive>
			inline
			bool load(IArchive& in) {
				return request::load(in) &&
					in.load(&m_table_name);
			}

			template<typename OArchive>
			inline
			bool save(OArchive& out) const {
				return request::save(out) &&
					out.save(m_table_name);
			}

			std::string to_string() const;
		};

		struct table_info_response
			: public response
		{
			static const type_code type = TABLE_INFO_RESP;
			
			table_info m_table_info;

			template<typename IArchive>
			inline
			bool load(IArchive& in) {
				return response::load(in) &&
					in.load(&m_table_info);
			}

			template<typename OArchive>
			inline
			bool save(OArchive& out) const {
				return response::save(out) &&
					out.save(m_table_info);
			}

			std::string to_string() const;
		};

		struct scan_open_request
			: public request
		{
			static const type_code type = SCAN_OPEN_REQ;

			typedef std::vector<std::string> column_name_vector;

			std::string m_tablet_name;
			std::string m_start_row;
			std::string m_end_row;
			column_name_vector m_column_names;
			uint64_t m_latest_time_stamp;
			uint64_t m_oldest_time_stamp;
			uint32_t m_ttl;
			uint32_t m_version_number;
			uint32_t m_options;
			uint32_t m_num_rows;
			
			uint32_t m_row_offset;
			uint32_t m_row_count;

			scan_open_request() {}

			template<typename IArchive>
			inline
			bool load(IArchive& in) {
				return request::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_start_row) &&
					in.load(&m_end_row) &&
					in.load(&m_column_names) &&
					in.load(&m_latest_time_stamp) &&
					in.load(&m_oldest_time_stamp) &&
					in.load(&m_ttl) &&
					in.load(&m_version_number) &&
					in.load(&m_options) &&
					in.load(&m_num_rows);
					//in.load(&m_row_offset) &&
					//in.load(&m_row_count);
			}

			template<typename OArchive>
			inline
			bool save(OArchive& out) const {
				return request::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_start_row) &&
					out.save(m_end_row) &&
					out.save(m_column_names) &&
					out.save(m_latest_time_stamp) &&
					out.save(m_oldest_time_stamp) &&
					out.save(m_ttl) &&
					out.save(m_version_number) &&
					out.save(m_options) &&
					out.save(m_num_rows);
					//out.save(m_row_offset) &&
					//out.save(m_row_count);
			}

			std::string to_string () const;
		};

		struct scan_open_response
			: public response
		{
			static const type_code type = SCAN_OPEN_RESP;

			std::string m_tablet_name;
			uint64_t m_scanner_id;
			
			uint32_t m_row_offset;

			scan_open_response() {}

			template<typename IArchive>
			inline
			bool load(IArchive& in) {
				return response::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_scanner_id);
					//in.load(&m_row_offset);
			}

			template<typename OArchive>
			inline
			bool save(OArchive& out) const {
				return response::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_scanner_id);
					//out.save(m_row_offset);
			}

			std::string to_string () const;
		};

		struct scan_next_request
			: public request
		{
			static const type_code type = SCAN_NEXT_REQ;

			std::string m_tablet_name;
			uint64_t m_scanner_id;

			scan_next_request() {}

			explicit
			scan_next_request(const std::string &tablet_name, uint64_t scanner_id)
			: m_tablet_name(tablet_name), m_scanner_id(scanner_id) {}

			template<typename IArchive>
			inline
			bool load(IArchive& in) {
				return request::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_scanner_id);
			}

			template<typename OArchive>
			inline
			bool save(OArchive& out) const {
				return request::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_scanner_id);
			}

			std::string to_string () const;
		};

		struct scan_next_response
			: public response
		{
			static const type_code type = SCAN_NEXT_RESP;

			uint64_t m_scanner_id;
			std::string m_tablet_name;
			record_vector m_records;

			template<typename IArchive>
			inline
			bool load(IArchive& in) {
				return response::load(in) &&
					in.load(&m_scanner_id) &&
					in.load(&m_tablet_name) &&
					in.load(&m_records);
			}

			template<typename OArchive>
			inline
			bool save(OArchive& out) const {
				return response::save(out) &&
					out.save(m_scanner_id) &&
					out.save(m_tablet_name) &&
					out.save(m_records);
			}

			std::string to_string () const;
		};

		struct scan_close_request
			: public request
		{
			static const type_code type = SCAN_CLOSE_REQ;

			std::string m_tablet_name;
			uint64_t m_scanner_id;

			scan_close_request() {}

			explicit
			scan_close_request(const std::string &tablet_name, uint64_t scanner_id)
			: m_tablet_name(tablet_name), m_scanner_id(scanner_id) {}

			template<typename IArchive>
			inline
			bool load(IArchive& in) {
				return request::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_scanner_id);
			}

			template<typename OArchive>
			inline
			bool save(OArchive& out) const {
				return request::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_scanner_id);
			}

			std::string to_string () const;
		};

		struct scan_close_response
			: public response
		{
			static const type_code type = SCAN_CLOSE_RESP;

			std::string m_tablet_name;
			uint64_t m_scanner_id;
			
			uint64_t m_row_count;

			template<typename IArchive>
			inline
			bool load(IArchive& in) {
				return response::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_scanner_id);
					//in.load(&m_row_count);
			}

			template<typename OArchive>
			inline
			bool save(OArchive& out) const {
				return response::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_scanner_id);
					//out.save(m_row_count);
			}

			std::string to_string () const;
		};

		struct batch_write_request
			: public request
		{
			static const type_code type = BATCH_WRITE_REQ;

			std::string m_tablet_name;
			std::vector<row_data> m_rows;

			void remove_success_row(std::size_t num) {
				const std::vector<row_data>::iterator start = m_rows.begin();
				const std::vector<row_data>::iterator end = start + num;
				m_rows.erase(start, end);
			}

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_rows);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_rows);
			}

			std::string to_string() const;
		};

		struct batch_write_response
			: public response
		{
			static const type_code type = BATCH_WRITE_RESP;

			std::string m_tablet_name;
			std::vector<uint32_t> m_status;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_status);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_status);
			}

			std::string to_string() const;
		};

		struct batch_write_request_v2
			: public request
		{
			static const type_code type = BATCH_WRITE_REQ;

			std::string m_tablet_name;
			std::vector<row_data_v2> m_rows;

			void remove_success_row(std::size_t num) {
				const std::vector<row_data_v2>::iterator start = m_rows.begin();
				const std::vector<row_data_v2>::iterator end = start + num;
				m_rows.erase(start, end);
			}

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_rows);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_rows);
			}

			std::string to_string() const;
		};

		struct batch_write_response_v2
			: public response
		{
			static const type_code type = BATCH_WRITE_RESP;

			uint32_t m_success_row;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_success_row);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_success_row);
			}

			std::string to_string() const;
		};

		struct batch_write_request_v2_for_tabletserver
			: public request
		{
			static const type_code type = WRITE_REQ;

			std::string m_tablet_name;
			std::vector<row_data_v2_for_tabletserver> m_rows;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_rows);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return false;
			}

			std::string to_string() const;
		};
		
		struct tablet_server_report
			: public request
		{
			static const type_code type = TABLET_SERVER_REPORT;

			server_address m_server_address;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_server_address);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_server_address);
			}

			std::string to_string() const;
		};

		struct heartbeat_request
			: public request
		{
			static const type_code type = HEARTBEAT_REQ;

			tablet_server_info m_tablet_server_info;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_tablet_server_info);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_tablet_server_info);
			}

			std::string to_string() const;
		};

		struct load_tablet_request
			: public request
		{
			static const type_code type = LOAD_TABLET_REQ;

			tablet_info m_tablet_info;
			table_info m_table_info;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_tablet_info) &&
					in.load(&m_table_info);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_tablet_info) &&
					out.save(m_table_info);
			}

			std::string to_string() const;
		};

		struct load_tablet_response
			: public response
		{
			static const type_code type = LOAD_TABLET_RESP;

			std::string m_tablet_name;
			server_address m_server_address;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_server_address);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_server_address);
			}

			std::string to_string() const;
		};

		struct remove_tablet_request
			: public request
		{
			static const type_code type = REMOVE_TABLET_REQ;

			std::string m_tablet_name;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_tablet_name);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_tablet_name);
			}

			std::string to_string() const;
		};

		struct remove_tablet_response
			: public response
		{
			static const type_code type = REMOVE_TABLET_RESP;

			std::string m_tablet_name;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_tablet_name);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_tablet_name);
			}

			std::string to_string() const;
		};

		struct tablet_split_report
			: public request
		{
			static const type_code type = TABLET_SPLIT_REPORT;

			tablet_info m_parent_tablet;
			tablet_info m_top_tablet;
			tablet_info m_bottom_tablet;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_parent_tablet) &&
					in.load(&m_top_tablet) &&
					in.load(&m_bottom_tablet);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_parent_tablet) &&
					out.save(m_top_tablet) &&
					out.save(m_bottom_tablet);
			}

			std::string to_string() const;
		};

		struct tablet_server_quit
			: public request
		{
			static const type_code type = TABLET_SERVER_QUIT;

			server_address m_server_address;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_server_address);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_server_address);
			}

			std::string to_string() const;
		};

		struct list_tablet_request
			: public request
		{
			static const type_code type = LIST_TABLET_REQ;
		};

		struct list_tablet_response
			: public response
		{
			static const type_code type = LIST_TABLET_RESP;

			std::vector<tablet_info> m_tablet_infos;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_tablet_infos);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_tablet_infos);
			}

			std::string to_string() const;
		};

		struct create_table_request
			: public request
		{
			static const type_code type = CREATE_TABLE_REQ;

			table_info m_table_info;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_table_info);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_table_info);
			}

			std::string to_string() const;
		};

		struct create_table_response
			: public response
		{
			static const type_code type = CREATE_TABLE_RESP;

			std::string m_table_name;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_table_name);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_table_name);
			}

			std::string to_string() const;
		};

		struct delete_table_request
			: public request
		{
			static const type_code type = DELETE_TABLE_REQ;

			std::string m_table_name;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_table_name);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_table_name);
			}

			std::string to_string() const;
		};

		struct delete_table_response
			: public response
		{
			static const type_code type = DELETE_TABLE_RESP;
			
			std::string m_table_name;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_table_name);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_table_name);
			}

			std::string to_string() const;
		};

		struct read_request
			: public request
		{
			static const type_code type = READ_REQ;

			std::string m_tablet_name;
			key m_key;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_key);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_key);
			}

			std::string to_string() const;
		};

		struct read_response
			: public response
		{
			static const type_code type = READ_RESP;

			std::string m_tablet_name;
			record_vector m_records;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_tablet_name) &&
					in.load(&m_records);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_tablet_name) &&
					out.save(m_records);
			}

			std::string to_string() const;
		};

		struct list_table_request
			: public request
		{
			static const type_code type = LIST_TABLE_REQ;

			std::string m_start_table_name;
			uint32_t m_table_number;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return request::load(in) &&
					in.load(&m_start_table_name) &&
					in.load(&m_table_number);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return request::save(out) &&
					out.save(m_start_table_name) &&
					out.save(m_table_number);
			}

			std::string to_string() const;
		};

		struct list_table_response
			: public response
		{
			static const type_code type = LIST_TABLE_RESP;

			std::vector<std::string> m_table_names;

			template<typename IArchive>
			inline
			bool load(IArchive &in) {
				return response::load(in) &&
					in.load(&m_table_names);
			}

			template<typename OArchive>
			inline
			bool save(OArchive &out) const {
				return response::save(out) &&
					out.save(m_table_names);
			}

			std::string to_string() const;
		};

#define def_packet(body) template<>					\
		struct packet_type<body::type>				\
		{							\
			typedef basic_packet<body> type;		\
			typedef boost::shared_ptr<type> ptr_type;	\
		}

		def_packet(root_meta_request);
		def_packet(root_meta_response);
		def_packet(write_request_v2);
		def_packet(write_response_v2);
		def_packet(table_info_request);
		def_packet(table_info_response);
		def_packet(scan_open_request);
		def_packet(scan_open_response);
		def_packet(scan_next_request);
		def_packet(scan_next_response);
		def_packet(scan_close_request);
		def_packet(scan_close_response);
		def_packet(batch_write_request_v2);
		def_packet(batch_write_response_v2);
		def_packet(tablet_server_report);
		def_packet(heartbeat_request);
		def_packet(load_tablet_request);
		def_packet(load_tablet_response);
		def_packet(remove_tablet_request);
		def_packet(remove_tablet_response);
		def_packet(tablet_split_report);
		def_packet(tablet_server_quit);
		def_packet(list_tablet_request);
		def_packet(list_tablet_response);
		def_packet(create_table_request);
		def_packet(create_table_response);
		def_packet(delete_table_request);
		def_packet(delete_table_response);
		def_packet(read_request);
		def_packet(read_response);
		def_packet(list_table_request);
		def_packet(list_table_response);

#undef def_packet_type

	} // namespace protocol

} // namespace xbase

#endif	// _XBASE_PROTOCOL_PACKET_HPP_
